import type {
  Expression, Token, Node, Identifier, Literal,
} from '@/types/retrieval-sql'
import { syntaxError, typeError } from '../../utils'
import ExpressionTarget from './ExpressionTarget'
import { joinExpression } from '@/utils/retrieval-sql/core/parser/index'

/**
 * 对 Token[] 进行遍历解析内容
 */
class Target {
  // 解析优先级时需要 Target 套 Target。当 target = undefined 时本身为根节点，否则只解析同级和子级内容
  // eslint-disable-next-line no-use-before-define
  readonly root: Target | undefined

  readonly tokens: Token[]

  // 当前 index
  private _current = -1

  // 当前值
  private _token: Token | undefined

  expression: Expression | undefined

  // 括号数组，（ +1 ， ）-1
  bracket: Token[] = []

  constructor(target: Token[] | Target) {
    if (Array.isArray(target)) {
      this.tokens = target
      this.next()
    } else {
      this.tokens = target.tokens
      this.root = target.root ?? target
    }
  }

  get current() {
    return this.root?._current ?? this._current
  }

  get token() {
    return this.root?._token ?? this._token
  }

  set token(value: Token | undefined) {
    if (this.root) {
      this.root._token = value
    }
    this._token = value
  }

  hasNext() {
    return this.current < this.tokens.length
  }

  next(filterWhitespace = true) {
    const nextToken = () => {
      if (this.root) {
        this.root.next()
      } else {
        this.token = this.tokens[++this._current]
      }
    }
    nextToken()
    while (filterWhitespace && this.hasNext() && this.token?.type === 'Whitespace') {
      nextToken()
    }
    return this.hasNext()
  }

  getToken(next = 0, filterWhitespace = true): Token | undefined {
    if (!filterWhitespace) {
      return this.tokens[this.current + next]
    }
    let count = 0
    let current = this.current
    while (this.tokens[current]) {
      if (count === next) {
        return this.tokens[current]
      } if (next > 0) {
        current += 1
        if (this.tokens[current]?.type !== 'Whitespace') {
          count += 1
        }
      } else if (next < 0) {
        current -= 1
        if (this.tokens[current]?.type !== 'Whitespace') {
          count -= 1
        }
      } else {
        return this.tokens[this.current + next]
      }
    }
    return undefined
  }

  lastToken() {
    let current = this.tokens.length - 1
    while (current >= 0 && this.tokens[current]?.type === 'Whitespace') {
      current -= 1
    }
    return this.tokens[current] || <Token>{
      type: 'Whitespace',
      start: 0,
      end: 0,
      value: '',
    }
  }

  startBracket() {
    this.bracket.push(this.token!)
  }

  endBracket() {
    this.bracket.pop()
  }

  walk(): Node | null {
    let childStop = false

    // 开始解析 token节点，把结点放入 ast 中。
    while (this.hasNext() && !childStop) {
      // eslint-disable-next-line no-use-before-define
      const bool = walks.every((handler) => handler.call(this))

      if (!bool && this.root) {
        childStop = true
      }
    }

    if (this.bracket.length) {
      const bracket = this.bracket[this.bracket.length - 1]
      throw syntaxError({
        message: '未结束的括号',
        start: bracket.start,
        end: bracket.end,
      })
    }

    // 最后我们的语法分析器返回 AST
    return this.expression || null
  }
}

function walkExpression(this: Target): boolean {
  const firstType = this.token?.type
  // 查询字段解析 "TI" = (标题)
  if (firstType !== 'Identifier') {
    throw syntaxError({
      message: `缺少查询字段 ${this.token!.value}`,
      start: this.token!.start,
      end: this.token!.end,
    })
  }

  // 关系运算符判定
  let identifier = this.token!

  // (精确) 判定
  if (this.getToken(1, false)?.value === '(' && this.getToken(2, false)?.value === '精确' && this.getToken(3, false)?.value === ')') {
    this.next()
    this.next()
    this.next()
    identifier = {
      type: 'Identifier',
      value: `${identifier.value}(精确)`,
      start: identifier.start,
      end: this.token!.end,
    }
  }

  // 关系运算符判定
  if (!this.next() || this.token!.type !== 'Punctuator' || !['=', '>', '<'].includes(this.token!.value)) {
    if (!this.root && !this.expression) {
      const token: Token = {
        type: 'Identifier',
        start: 0,
        end: 0,
        value: 'S7',
      }
      const start = identifier.start
      const end = identifier.end
      const target = new ExpressionTarget(this, token, '=')
      target.expression = <Expression<Identifier, Literal>>{
        type: 'Expression',
        operation: '=',
        variant: 'operation',
        left: {
          type: 'Identifier',
          variant: 'column',
          name: 'S7',
          start,
          end,
        },
        right: {
          type: 'Literal',
          variant: 'text',
          value: identifier.value,
          start,
          end,
        },
        start,
        end,
      }
      this.expression = target.walk()
      return true
    }

    throw syntaxError({
      message: '缺少比较运算符 "= > <"',
      start: identifier.start,
      end: identifier.end,
    })
  }

  let operation = this.token!.value
  if (!['=', '>', '<'].includes(operation)) {
    throw syntaxError({
      message: '缺少比较运算符"= < >"',
      start: this.token!!.start,
      end: this.token!!.end,
    })
  }
  this.next()

  // 等号判断 TI >"=" (标题)
  if (this.token?.type === 'Punctuator' && this.token?.value === '=') {
    this.next()
    operation = `${operation}=`
  }

  // 开始解析查询值
  const expression = new ExpressionTarget(this, identifier, operation as any).walk()
  if (!this.expression) {
    // 开始
    this.expression = expression
  } else if (!this.expression.right) {
    // 逻辑运算符连接处理
    this.expression.right = expression
    this.expression.end = expression.end
  } else if (this.expression.right.type === 'Expression' && !this.expression.right.right) {
    // 优先级的结构转换
    this.expression.right.right = expression
    this.expression.right.end = expression.end
  } else {
    throw typeError({
      message: '内部错误，未知的结构',
      start: expression.start,
      end: expression.end,
    })
  }
  return true
}
function walkParcelBracket(this: Target): boolean {
  const { bracket } = this

  // 解析括号处理
  while (this.token?.type === 'Punctuator' || this.token?.type === 'Whitespace') {
    if (this.token.type === 'Whitespace') {
      this.next()
      continue
    }
    const token = this.token!
    if (token.value === '(') {
      if (this.getToken(-1)?.value === ')') {
        throw syntaxError({
          message: '缺少逻辑运算符',
          start: token.start,
          end: token.end,
        })
      }
      this.startBracket()
      this.next()
      continue
    }

    if (token.value === ')') {
      // 括号成对出现
      if (bracket.length === 0) {
        throw syntaxError({
          message: '无开始括号。如查询值带括号需成对出现',
          start: token.start,
          end: token.end,
        })
      }
      if (this.getToken(-1)?.value === '(') {
        throw syntaxError({
          message: '括号内容为空',
          start: this.getToken(-1)!.start,
          end: token.end,
        })
      }
      this.endBracket()
      this.next()

      if (bracket.length === 0 && this.root) {
        return false
      }

      continue
    }

    throw syntaxError({
      message: '符号使用错误',
      start: token.start,
      end: token.end,
    })
  }
  return true
}
function walkJoin(this: Target, required = true): boolean {
  let token = this.token

  if (!token) {
    return false
  }

  if (token?.type === 'Whitespace') {
    this.next()
  }

  if (!this.getToken(1)) {
    throw syntaxError({
      message: '缺少后续查询',
      start: this.lastToken().start,
      end: this.lastToken().end,
    })
  }

  if (!this.expression) {
    throw syntaxError({
      message: '逻辑运算符不能在开始位置',
      start: token.start,
      end: token.end,
    })
  }

  if (token.type !== 'Keyword') {
    if (!required) {
      return true
    }

    throw syntaxError({
      message: '缺少逻辑运算符 AND/NOT/OR',
      start: token.start,
      end: token.end,
    })
  }

  this.expression = joinExpression(this.expression, token, this.getToken(-1))
  this.next()

  // 判断括号优先级
  token = this.token
  if (token?.type === 'Punctuator' && token.value === '(') {
    const expression = new Target(this).walk()
    if (!expression) {
      throw syntaxError({
        message: '缺少查询内容',
        start: token.start,
        end: this.lastToken().end,
      })
    }
    if (!this.expression.right) {
      this.expression.right = expression
    } else {
      (this.expression.right as Expression).right = expression
    }
    // 判断是否还有后续结果
    walkParcelBracket.call(this)
    walkJoin.call(this, false)
  }
  return true
}

const walks: ((this: Target) => boolean)[] = [
  walkParcelBracket, // ((
  walkExpression, // ti = xx
  walkParcelBracket, // ))
  walkJoin, // 该位置会处理优先级进行递归
]
export default Target
